import { bomApi } from '@/api';
import { defaultDiffConfig, getDiffConfig, updateDiffConfig } from '@/api/diff';
import { DiffColorize } from '@/components/DiffColorize';
import { columns as bomColumns } from '@/constants/bom';
import { useLog } from '@/hooks/console';
import useSearchFilter from '@/hooks/table';
import { IPlank } from '@/types';
import { dateFormat } from '@/utils/date';
import {
  AnyRecord,
  arrayDiffFix,
  createRecord,
  diffOptions,
  diffTextMap,
  EDiffType,
  getChangedSourceValueOfDiffItemsV2,
  getDiffContentLite,
  getDiffList,
  getShallowDiffList,
  getSourceItem,
  getSourceItemValue,
  IDiffItemWithSourceData,
  isChangedDiffItem,
  ISourceItem,
} from '@/utils/diff';
import { download } from '@/utils/file';
import { waitTime } from '@/utils/promise';
import { filterCol } from '@/utils/table';
import { history, useModel } from '@umijs/max';
import { useDebounceEffect, useRequest, useSetState } from 'ahooks';
import { Button, message, Modal as AModal, Space, Table, Tooltip } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { compact, find, isEmpty, map, unset } from 'lodash-es';
import {
  ComponentProps,
  ReactChild,
  useEffect,
  useMemo,
  useState,
} from 'react';
import { Unless } from 'react-if';
import DiffConfig from '../DiffConfig';
import JumpBomView from '../JumpBomView';

const log = console.log.bind(null, '[DataDiff]');
function toHome() {
  history.push({ pathname: '/home' });
}

export enum EHiddenPart {
  /** 更新基准数据 */
  UpdateBase,
}

interface Props {
  oldPlanks: IPlank[];
  newPlanks: IPlank[];
  beforeOpen?(): Promise<void>;
  extraBtns?: ReactChild;
  onRefresh?: () => Promise<void>;
  hiddenParts?: EHiddenPart[];
}
export function DataDiffTable(props: Props) {
  const { hiddenParts } = props;
  function shouldHiddenPart(part: EHiddenPart) {
    if (!hiddenParts) return false;
    return hiddenParts.includes(part);
  }
  const {
    design: { designId },
  } = useModel('bom');
  const { oldPlanks: oldPlanks, newPlanks: newPlanks } = props;
  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
  const [loading, setLoading] = useState(false);
  const {
    data: diffConfig = defaultDiffConfig,
    refreshAsync: refreshDiffConfig,
  } = useRequest(getDiffConfig);

  const {
    data: markedPlankExtraInfoList = [],
    runAsync: getMarkedPlankExtraInfoList,
  } = useRequest(
    async () => {
      const extraInfos = await bomApi.getMarkedPlankExtraInfoList({ designId });
      return extraInfos;
    },
    {
      manual: true,
    },
  );
  function getMarkedPlankUpdateTime(params: { modelId: string }) {
    const { modelId } = params;
    const extraInfo = find(markedPlankExtraInfoList, { modelId });
    return extraInfo?.updateTime;
  }

  interface IDiffRecordItem<T extends AnyRecord> {
    status: boolean;
    sourceItem: ISourceItem<T>;
    result?: IDiffItemWithSourceData<T>;
  }
  type IDiffRecord<T extends AnyRecord> = {
    [modelId: string]: IDiffRecordItem<T>;
  };
  const [diffRecord, setDiffRecord] = useSetState(() =>
    createRecord<IDiffRecord<IPlank>>(),
  );
  function hasChangedDiff(diffRecord: IDiffRecord<IPlank>) {
    return Object.values(diffRecord).some((diff) => {
      if (!diff.result) return;
      return isChangedDiffItem(diff.result);
    });
  }
  function showDiffMsg() {
    message.error('板件有差异');
  }
  function handleDiffRecordChanged() {
    if (hasChangedDiff(diffRecord)) {
      showDiffMsg();
    }
  }
  useDebounceEffect(() => {
    handleDiffRecordChanged();
  }, [diffRecord]);
  async function handleGetDiffs(params: { modelIds: string[] }) {
    setLoading(true);
    await waitTime(100);
    const { modelIds } = params;
    modelIds.map((modelId) => {
      const oldList: IPlank[] = [];
      const newList: IPlank[] = [];
      const newPlank = find(newPlanks, { modelId });
      const oldPlank = find(oldPlanks, { modelId });
      if (oldPlank) {
        oldList.push(oldPlank);
      }
      if (newPlank) {
        newList.push(newPlank);
      }
      if (!oldPlank && !newPlank) {
        unset(diffRecord, modelId);
        return;
      }
      const [diffResult] = getDiffList(
        oldList,
        newList,
        diffConfig.key,
        diffConfig.omitKeys,
      );
      const [type, content, source] = diffResult;
      if (type === EDiffType.Modified && content.holes) {
        const holeDiffResult = arrayDiffFix(
          source.oldItem?.holes ?? [],
          source.newItem?.holes ?? [],
          diffConfig.omitKeys,
        );
        content.holes = holeDiffResult;
        // console.log({ diffResult, holeDiffResult });
      }
      diffRecord[modelId] = {
        status: true,
        sourceItem: getSourceItem(diffResult),
        result: diffResult,
      };
    });
    log({ diffRecord });
    setDiffRecord(diffRecord);
    setLoading(false);
  }
  function handleGetAllDiffs() {
    const modelIds = dataSource.map((item) => item.modelId);
    handleGetDiffs({ modelIds });
  }

  function resetDiffRecord() {
    Object.keys(diffRecord).map((key) => {
      unset(diffRecord, key);
    });
  }

  function init() {
    resetDiffRecord();
    const { diffList, unDiffList } = getShallowDiffList(
      oldPlanks,
      newPlanks,
      diffConfig.key,
    );
    diffList.map((diff) => {
      const sourceItem = getSourceItem(diff);
      const modelId = getSourceItemValue(sourceItem, 'modelId');
      diffRecord[modelId] = { status: true, sourceItem, result: diff };
    });
    unDiffList.map((sourceItem) => {
      const modelId = getSourceItemValue(sourceItem, 'modelId');
      diffRecord[modelId] = { status: false, sourceItem };
    });
    log({ diffRecord });
    setDiffRecord(diffRecord);
  }

  useEffect(() => {
    init();
    // log({ diffRecord });
  }, [oldPlanks, newPlanks]);

  useLog(
    { oldData: oldPlanks, newData: newPlanks, diffRecord },
    {
      log,
    },
  );

  interface IUpdateMarkedPlankListParams {
    modelIds: string[];
  }

  async function handleUpdateMarkedPlankList_1(
    params: IUpdateMarkedPlankListParams,
  ) {
    setLoading(true);
    const { modelIds } = params;
    const unUpdatedPlanksPlanks = oldPlanks.filter(
      (plank) => !modelIds.includes(plank.modelId),
    );
    const updatedPlanks = newPlanks.filter((plank) =>
      modelIds.includes(plank.modelId),
    );
    const allPlanks = [...unUpdatedPlanksPlanks, ...updatedPlanks];
    await bomApi.updateMarkedPlankList({
      designId,
      planks: allPlanks,
    });
    await bomApi.updateMarkedPlankExtraInfoList({
      designId,
      planks: updatedPlanks,
    });
    oldPlanks.splice(0, oldPlanks.length, ...allPlanks);
    await handleGetDiffs({ modelIds });
    setLoading(false);
  }

  async function handleUpdateMarkedPlankList(
    params: IUpdateMarkedPlankListParams,
  ) {
    return new Promise<void>((res) => {
      AModal.confirm({
        title: '确认更新基准数据吗？',
        async onOk() {
          await handleUpdateMarkedPlankList_1(params);
          res();
        },
        onCancel() {
          res();
        },
      });
    });
  }

  const columns: ColumnsType<IPlank> = [
    {
      title: '差异',
      key: 'diff',
      filters: diffOptions.map(({ label, value }) => {
        return { text: label, value };
      }),
      onFilter: (value, record) => {
        const [diffType] = diffRecord[record.modelId].result || [];
        return diffType === value;
      },
      render(_value, record) {
        const { modelId } = record;
        const diffRecordItem = diffRecord[modelId];
        if (!diffRecordItem.status) return <></>;
        const [diffType] = diffRecordItem.result!;
        // if (diffType === EDiffType.Unchanged) return <></>;
        return <strong>{diffTextMap[diffType]}</strong>;
      },
    },
    {
      title: '基准plankId',
      key: 'old_plankId',
      render(_value, record) {
        const { modelId } = record;
        const { sourceItem } = diffRecord[modelId]!;
        return sourceItem.oldItem?.plankId;
      },
    },
    {
      title: '最新plankId',
      key: 'new_plankId',
      render(_value, record) {
        const { modelId } = record;
        const { sourceItem } = diffRecord[modelId]!;
        return sourceItem.newItem?.plankId;
      },
    },
    ...filterCol(bomColumns, {
      dataIndexs: [
        'code',
        'modelId',
        'name',
        // 'plankId',
        'orderId',
        'orderName',
        'productName',
      ],
    }),
    /* {
      title: '执行状态',
      key: 'status',
      render(value, record, index) {
        const { modelId } = record;
        const { status } = diffRecord[modelId];
        return status + '';
      },
    }, */
    {
      title: '更新时间',
      dataIndex: 'updateTime',
      render(value, record, index) {
        const { modelId } = record;
        const updateTime = getMarkedPlankUpdateTime({ modelId });
        if (!updateTime) return;
        return dateFormat(updateTime);
      },
    },
    {
      title: '操作',
      key: 'preview',
      render(_value, record) {
        const { modelId } = record;
        const diffRecordItem = diffRecord[modelId];
        // if (!diffRecordItem.status) return <></>;
        const isUnDiffed = !diffRecordItem.status;
        const { result, sourceItem } = diffRecord[modelId]!;
        const [diffType, diffData] = result! || [];
        let newItem = sourceItem.newItem!;
        let oldItem = sourceItem.oldItem!;

        function getDefaultHeightLight() {
          return {
            holes: [],
            grooves: [],
          };
        }

        function getHeightLight() {
          if (diffType !== EDiffType.Modified) return getDefaultHeightLight();

          const highlight = {
            holes: diffData.holes
              ? getChangedSourceValueOfDiffItemsV2(
                  diffData.holes,
                  {
                    oldItems: oldItem.holes,
                    newItems: newItem.holes,
                  },
                  'holeId',
                )
              : [],
            grooves: diffData.grooves
              ? getChangedSourceValueOfDiffItemsV2(
                  diffData.grooves,
                  {
                    oldItems: oldItem.grooves,
                    newItems: newItem.grooves,
                  },
                  'grooveId',
                )
              : [],
          };
          return highlight;
        }

        const tryGetHeightLight = () => {
          try {
            return getHeightLight();
          } catch (error) {
            log('error', error);
            return getDefaultHeightLight();
          }
        };

        let highlight = tryGetHeightLight();
        // if (values(highlight).some(negate(isEmpty))) {
        // log({ highlight });
        // }

        return (
          <>
            <Button
              type="link"
              size="small"
              onClick={() => handleGetDiffs({ modelIds: [modelId] })}
            >
              执行
            </Button>
            {true && (
              // <JumpBomView
              //   orderId={record.orderId}
              //   plankId={record.plankId}
              // >
              <Button
                disabled={isUnDiffed || !(diffType !== EDiffType.Added)}
                type="link"
                size="small"
                onClick={() =>
                  download(
                    JSON.stringify(record, null, 4),
                    `${modelId}_base.json`,
                  )
                }
              >
                基准数据
              </Button>
              // </JumpBomView>
            )}
            {true && (
              // <JumpBomView
              //   orderId={record.orderId}
              //   plankId={newData.plankId}
              // >
              <Button
                disabled={isUnDiffed || !(diffType !== EDiffType.Removed)}
                type="link"
                size="small"
                onClick={() =>
                  download(
                    JSON.stringify(newItem, null, 4),
                    `${modelId}_new.json`,
                  )
                }
              >
                对比数据
              </Button>
              // </JumpBomView>
            )}
            {diffType !== EDiffType.Unchanged ? (
              <Button
                disabled={isUnDiffed}
                // disabled={!(diffType !== EDiffType.Unchanged)}
                danger
                type="link"
                size="small"
                onClick={() =>
                  download(
                    JSON.stringify(
                      getDiffContentLite(diffData, 'holes'),
                      null,
                      4,
                    ),
                    `${modelId}_diff.json`,
                  )
                }
              >
                <Tooltip
                  title={
                    <DiffColorize
                      className="diff-colorize h-screen overflow-y-auto"
                      diff={diffData}
                    />
                  }
                >
                  对比失败
                </Tooltip>
              </Button>
            ) : (
              <span className="ant-btn-sm color-success">对比成功</span>
            )}
            <Unless condition={shouldHiddenPart(EHiddenPart.UpdateBase)}>
              <Button
                type="link"
                size="small"
                onClick={async () => {
                  const { modelId } = record;
                  await handleUpdateMarkedPlankList({
                    modelIds: [modelId],
                  });
                }}
              >
                更新基准
              </Button>
            </Unless>
            <JumpBomView
              orderId={oldItem?.orderId}
              plankId={oldItem?.plankId}
              plank={oldItem}
              highlight={highlight}
            >
              <Button disabled={!oldItem} type="link" size="small">
                预览基准数据
              </Button>
            </JumpBomView>
            <JumpBomView
              orderId={newItem?.orderId}
              plankId={newItem?.plankId}
              plank={newItem}
              highlight={highlight}
            >
              <Button disabled={!newItem} type="link" size="small">
                预览对比数据
              </Button>
            </JumpBomView>
          </>
        );
      },
    },
  ];
  const columnsWithSearch = useSearchFilter({
    columns,
    dataIndexs: ['orderName', 'orderId', 'name', 'modelId'],
  });

  const dataSource = useMemo(() => {
    const dataSource = Object.values(diffRecord).map(({ sourceItem }) => {
      const item = compact([sourceItem.oldItem, sourceItem.newItem])[0];
      return item;
    });
    log({ dataSource });
    return dataSource;
  }, [diffRecord]);

  useEffect(() => {
    getMarkedPlankExtraInfoList();
  }, [dataSource]);

  async function handleUpdateDiffConfig(
    config: ComponentProps<typeof DiffConfig>['diffConfig'],
  ) {
    await updateDiffConfig(config);
    await refreshDiffConfig();
  }

  return (
    <>
      <div className="btns">
        <Space>
          <Button type="primary" onClick={handleGetAllDiffs}>
            全部执行
          </Button>
          <Button
            disabled={isEmpty(selectedRowKeys)}
            type="primary"
            onClick={() => {
              handleGetDiffs({ modelIds: selectedRowKeys });
            }}
          >
            批量执行
          </Button>
          <Unless condition={shouldHiddenPart(EHiddenPart.UpdateBase)}>
            <Button
              disabled={isEmpty(selectedRowKeys)}
              type="primary"
              onClick={() =>
                handleUpdateMarkedPlankList({ modelIds: selectedRowKeys })
              }
            >
              批量更新基准数据
            </Button>
            <Button
              type="primary"
              onClick={() =>
                AModal.confirm({
                  title: '确认全量覆盖基准数据吗？',
                  onOk: () => {
                    log('ok');
                    handleUpdateMarkedPlankList({
                      modelIds: map(newPlanks, 'modelId'),
                    });
                  },
                })
              }
            >
              全量覆盖基准数据
            </Button>
          </Unless>
          {props.extraBtns}
        </Space>
        <Space className="float-right">
          <DiffConfig
            diffConfig={diffConfig}
            onUpdateConfig={handleUpdateDiffConfig}
          />
          <Button type="primary" onClick={toHome}>
            结束对比
          </Button>
        </Space>
      </div>
      <Table
        loading={loading}
        rowKey={'modelId'}
        rowSelection={{
          type: 'checkbox',
          selectedRowKeys,
          onChange(keys, record) {
            log({ keys, record });
            setSelectedRowKeys(keys as string[]);
          },
        }}
        size="small"
        dataSource={dataSource}
        columns={columnsWithSearch}
      />
    </>
  );
}
